﻿using MQTTnet;
using MQTTnet.Adapter;
using MQTTnet.Client;
using MQTTnet.Client.Connecting;
using MQTTnet.Client.Options;
using MQTTnet.Client.Publishing;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;

namespace IoTMqttSdk
{
    public class IoTMqttClient
    {
        private IMqttClient _MqttClient;
        private string checkInDeviceId;
        private int v1;
        private byte checkInSecret;
        private string v2;
        private string ioTAddress;
        private object ioTPort;
        private readonly string _ClientId;
        private readonly string _DeviceId;
        private readonly string _NodeId;
        private readonly byte _AuthType;
        private readonly string _Password;
        private readonly string _CertFilePath;
        private readonly string _MqttServer;
        private readonly int _MqttPort;
        private readonly string _TopicPublish;
        private readonly string _TopicSubscribe;

        public bool IsConnected => _MqttClient?.IsConnected == true;
        /// <summary>
        /// 
        /// </summary>
        /// <param name="deviceId">设备的deviceId</param>
        /// <param name="nodeId">表示使用一机一密设备的nodeId接入。</param>
        /// <param name="signType">密码签名类型：长度1字节，当前支持2种类型：“0”代表HMACSHA256不校验时间戳。“1”代表HMACSHA256校验时间戳。</param>
        /// <param name="secret">注册设备时平台返回的secret</param>
        /// <param name="certPath">证书文明路径</param>
        /// <param name="mqttServer">Mqtt 服务器地址</param>
        /// <param name="mqttPort">Mqtt 服务器 端口</param>
        public IoTMqttClient(string deviceId, string nodeId, byte signType, string secret, string certPath, string mqttServer, int mqttPort)
        {
            _AuthType = 2;//鉴权类型：长度1字节，当前支持的类型：“0”，表示使用一机一密设备的deviceId接入；“2”，表示使用一机一密设备的nodeId接入。
            var date = DateTime.UtcNow.ToString("yyyyMMddHH");
            _DeviceId = deviceId;
            _NodeId = nodeId;
            _CertFilePath = certPath;
            _MqttServer = mqttServer;
            _MqttPort = mqttPort;
            _ClientId = $"{deviceId}_{_AuthType}_{signType}_{date}";
            _Password = Encrypt(secret, date);
            _TopicPublish = $"/huawei/v1/devices/{_DeviceId}/data/json";
            _TopicSubscribe = $"/huawei/v1/devices/{_DeviceId}/command/json";
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="deviceId">设备的deviceId</param>
        /// <param name="signType">密码签名类型：长度1字节，当前支持2种类型：“0”代表HMACSHA256不校验时间戳。“1”代表HMACSHA256校验时间戳。</param>
        /// <param name="secret">注册设备时平台返回的secret</param>
        /// <param name="certPath">证书文明路径</param>
        /// <param name="mqttServer">Mqtt 服务器地址</param>
        /// <param name="mqttPort">Mqtt 服务器 端口</param>
        public IoTMqttClient(string deviceId, byte signType, string secret, string certPath, string mqttServer, int mqttPort)
        {
            _AuthType = 0;//鉴权类型：长度1字节，当前支持的类型：“0”，表示使用一机一密设备的deviceId接入；“2”，表示使用一机一密设备的nodeId接入。
            var date = DateTime.UtcNow.ToString("yyyyMMddHH");
            _DeviceId = deviceId;
            _CertFilePath = certPath;
            _MqttServer = mqttServer;
            _MqttPort = mqttPort;
            _ClientId = $"{deviceId}_{_AuthType}_{signType}_{date}";
            _Password = Encrypt(secret, date);
            _TopicPublish = $"/huawei/v1/devices/{_DeviceId}/data/json";
            _TopicSubscribe = $"/huawei/v1/devices/{_DeviceId}/command/json";
        }

        public IoTMqttClient(string checkInDeviceId, int v1, byte checkInSecret, string v2, string ioTAddress, object ioTPort)
        {
            this.checkInDeviceId = checkInDeviceId;
            this.v1 = v1;
            this.checkInSecret = checkInSecret;
            this.v2 = v2;
            this.ioTAddress = ioTAddress;
            this.ioTPort = ioTPort;
        }

        public async Task<MqttClientConnectResultCode> ConnectServerAsync(Action<ReceivedMessage> receiveMessageHandler, Action disconnect)
        {
            if (_MqttClient == null)
            {
                _MqttClient = new MqttFactory().CreateMqttClient();
                
                _MqttClient.UseConnectedHandler(async handle =>
                {
                    try
                    {                        
                        Console.WriteLine($"IoT连接:{handle.AuthenticateResult.ResultCode}");
                        var result = await _MqttClient.SubscribeAsync(new TopicFilter()
                        {
                            Topic = _TopicSubscribe,
                            QualityOfServiceLevel = MQTTnet.Protocol.MqttQualityOfServiceLevel.AtLeastOnce

                        });
                        Console.WriteLine($"订阅返回数:{result.Items.Count},结果Item[0]:{result.Items[0].ResultCode}");
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.Message);
                    }
                });
                _MqttClient.UseApplicationMessageReceivedHandler(handle =>
                {
                    try
                    {
                        var payload = Encoding.UTF8.GetString(handle.ApplicationMessage.Payload);
                        var data = JsonConvert.DeserializeObject<ReceivedMessage>(payload);
                        receiveMessageHandler(data);

                        //Console.WriteLine($"收到MSG：{payload}");

                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.Message);
                    }

                });
                _MqttClient.UseDisconnectedHandler(async handle =>
                {
                    disconnect();
                    //Console.WriteLine($"MQTT 断开已经连接，准备重连...");
                    await _MqttClient.ReconnectAsync();
                });
            }
            try
            {
                var options = new MqttClientOptionsBuilder()
                    .WithProtocolVersion(MQTTnet.Formatter.MqttProtocolVersion.V311)
                    .WithClientId(_ClientId)
                    .WithTcpServer(_MqttServer, _MqttPort) //"49.4.93.24"8883
                    .WithCredentials(_AuthType == 0 ? _DeviceId : _NodeId, _Password)
                    .WithKeepAlivePeriod(TimeSpan.FromSeconds(60))
                    .WithKeepAliveSendInterval(TimeSpan.FromSeconds(50))
                    .WithTls(new MqttClientOptionsBuilderTlsParameters()
                    {
                        AllowUntrustedCertificates = false,
                        UseTls = true,
                        Certificates = new List<byte[]> { new X509Certificate2(_CertFilePath).Export(X509ContentType.Cert) },
                        CertificateValidationCallback = delegate { return true; },
                        IgnoreCertificateChainErrors = false,
                        IgnoreCertificateRevocationErrors = false
                    })
                    .WithCleanSession()
                    .Build();

                var result = await _MqttClient.ConnectAsync(options);

                return result.ResultCode;
            }
            catch(MqttConnectingFailedException mex)
            {
                return mex.ResultCode;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                return MqttClientConnectResultCode.NotAuthorized;
            }
        }

        public async Task<MqttClientPublishReasonCode> ReportingDataAsync(SendMessage message)
        {
            var payload = JsonConvert.SerializeObject(message, new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() });
            var result = await _MqttClient.PublishAsync(_TopicPublish, payload);
            return result.ReasonCode;
        }

        public async Task<MqttClientPublishReasonCode> CommandResponseAsync(int mid, Dictionary<string, dynamic> body)
        {
            var json = JsonConvert.SerializeObject(body, new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() });
            var result = await _MqttClient.PublishAsync(_TopicPublish, $"{{\"msgType\":\"deviceRsp\",\"mid\":{mid},\"errcode\":0,\"body\":{json}}}");

            Console.WriteLine($"设备响应命令:{result.ReasonCode}");

            return result.ReasonCode;
        }
        public async Task DisconnectAsync()
        {
            if (_MqttClient.IsConnected)
                await _MqttClient.DisconnectAsync();
        }
        public async Task Reconnect()
        {
            if (!_MqttClient.IsConnected)
                await _MqttClient.ReconnectAsync();
        }
        string Encrypt(string message, string secret)
        {
            secret = secret ?? "";
            //var encoding = new System.Text.ASCIIEncoding();
            var encoding = new System.Text.UTF8Encoding();
            byte[] keyByte = encoding.GetBytes(secret);
            byte[] messageBytes = encoding.GetBytes(message);
            using (var hmacsha256 = new HMACSHA256(keyByte))
            {
                byte[] hashmessage = hmacsha256.ComputeHash(messageBytes);
                //return Convert.ToBase64String(hashmessage);
                StringBuilder rst = new StringBuilder();
                for (int i = 0; i < hashmessage.Length; i++)
                {
                    rst.Append(hashmessage[i].ToString("x2"));
                }
                return rst.ToString();
            }
        }

    }
}
